using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace CppCliWrapper
{
    public class ILBridgeGenerator : TypeGenerator
    {
        protected override void OnGenerate(Type type, StringBuilder outputText)
        {
            //This can all sit in an .h file.  Just need to make sure
            //that no unmanaged clients are #including this file.

            outputText.AppendLine("#pragma once");
            outputText.AppendLine("#pragma managed");
            outputText.AppendLine("#include <vcclr.h>");
            outputText.AppendLine("class " + Utils.GetILBridgeTypeNameFor(type) + " {");
            
            GenerateAggregatedMember(type, outputText);

            outputText.AppendLine("public:");
            GenerateConstructor(type, outputText);
            foreach (MethodInfo method in type.GetMethods())
            {
                if (method.DeclaringType == type)
                    GenerateMethodWrapper(method, outputText);
            }

            outputText.AppendLine("};");
        }

        private void GenerateAggregatedMember(Type type, StringBuilder builder)
        {
            builder.AppendLine("private:");
            builder.AppendLine("gcroot<" + Utils.GetCppCliTypeNameFor(type) + "^> __Impl;");
        }

        //TODO: Constructor parameters if necessary
        //TODO: Several constructors on the managed type
        private void GenerateConstructor(Type type, StringBuilder builder)
        {
            builder.AppendLine(Utils.GetILBridgeTypeNameFor(type) + "() {");
            builder.AppendLine("__Impl = gcnew " + Utils.GetCppCliTypeNameFor(type) + ";");
            builder.AppendLine("}");
        }

        private void GenerateMethodWrapper(MethodInfo method, StringBuilder builder)
        {
            if (method.IsStatic)
                builder.Append("static ");

            TypeConverter.TypeTranslation retValTranslation = TypeConverter.TranslateParameterType(method.ReturnType);
            builder.Append(retValTranslation.NativeType + " " + method.Name + "(");
            bool notFirst = false;
            foreach (ParameterInfo parameter in method.GetParameters())
            {
                if (notFirst)
                    builder.Append(", ");
                notFirst = true;
                builder.Append(TypeConverter.TranslateParameterType(parameter.ParameterType).NativeType + " " + parameter.Name);
            }
            builder.AppendLine(") {");
            
            foreach (ParameterInfo parameter in method.GetParameters())
            {
                string paramType = Utils.GetCppCliTypeNameFor(parameter.ParameterType);
                builder.Append(paramType + " " + Utils.GetLocalTempNameFor(parameter) + " = ");
                if (TypeConverter.TranslateParameterType(parameter.ParameterType).MarshalingRequired)
                {
                    builder.AppendLine("marshal_to<" + paramType + ">(" + parameter.Name + ");");
                }
                else
                {
                    builder.AppendLine(parameter.Name + ";");
                }
            }

            if (method.ReturnType != typeof(void))
            {
                builder.Append(Utils.GetCppCliTypeNameFor(method.ReturnType) + " __ReturnVal = ");
            }
            builder.Append("__Impl->" + method.Name + "(");
            notFirst = false;
            foreach (ParameterInfo parameter in method.GetParameters())
            {
                if (notFirst)
                    builder.Append(", ");
                notFirst = true;
                builder.Append(Utils.GetLocalTempNameFor(parameter));
            }
            builder.AppendLine(");");

            if (method.ReturnType != typeof(void))
            {
                if (retValTranslation.MarshalingRequired)
                {
                    builder.AppendLine(retValTranslation.NativeType + " __MarshaledReturnVal = marshal_to<" +
                                       retValTranslation.NativeType + ">(__ReturnVal);");
                    builder.AppendLine("return __MarshaledReturnVal;");

                    //TODO: Generate marshaler if needed?
                }
                else
                {
                    builder.Append("return __ReturnVal;");
                }
            }
            builder.AppendLine("}");
        }

        protected override string GetFileName(Type type)
        {
            return Utils.GetILBridgeTypeNameFor(type) + ".h";
        }
    }
}
